#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <winsock2.h>
#include "network.h"
#include "tinycthread.h"
#include "tool.h"

#pragma comment(lib, "ws2_32.lib")
SOCKET listenSocket;
#define MAX_ClIENT 6
#define MAX_QUEUE 20
#define CLIENT_MSG_HANDLER_MAX 10
int nowClientCount = 0;
int ServerOn = false;
mtx_t locker;
bool IDClientOnline[MAX_ClIENT];
bool ClientQueueBusy[MAX_ClIENT][MAX_QUEUE];
char ClientSendQueue[MAX_ClIENT][MAX_QUEUE][64];
//response handler
void(*收到客户端消息时的回调函数[CLIENT_MSG_HANDLER_MAX])(ClientMessage*); //测试中文变量名
int 回调函数的数量 = 0; //ResponseHandlers ResponseHandlerCount
void(*onClientDisconnect)(int id) = nullptr;

typedef struct{
    int id;
    SOCKET sock;
} ClientData;

ClientData* clientDatas[MAX_ClIENT];

int asyncSendQueueToClient(void* arg); //not for external call
int listenAndHandleClient(void* arg); //not for external call
int waitAndDistributeClient(void* arg); //not for external call

// 调用者主线程创建服务器的Socket，开始listen，
// 随后在新线程中waitAndDistributeClient
bool StartServer() {
    ServerOn = true;
    struct sockaddr_in serverAddr;
    char buffer[1024];
    // 创建监听socket
    if ((listenSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("[S]Failed to create listen socket\n");
        return false;
    }
    // 构建本地地址
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(PORT);
    // 绑定监听socket到本地地址
    if (bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        printf("[S]Failed to bind listen socket\n");
        closesocket(listenSocket);
        return false;
    }
    // 开始监听连接
    if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
        printf("[S]Failed to listen on socket\n");
        closesocket(listenSocket);
        return false;
    }
    printf("[S]Waiting for incoming connections...\n");
    mtx_init(&locker,mtx_plain);
    thrd_t accepter;
    thrd_create(&accepter, waitAndDistributeClient, NULL);
    return true;
}

void CloseServer()
{
    ServerOn = false;
    closesocket(listenSocket);
    nowClientCount = 0;
    for (size_t i = 0; i < MAX_ClIENT; i++)
    {
        if (IDClientOnline[i]) {
            closesocket(clientDatas[i]->sock);
            IDClientOnline[i] = false;
        }
        for (int j = 0; j < MAX_QUEUE; ++j) {
            ClientQueueBusy[i][j] = false;
        }
        delete clientDatas[i];
    }
    onClientDisconnect = nullptr;
    回调函数的数量 = 0;
    mtx_destroy(&locker);
}

bool QueueSendDataToClient(int id, char* data) {
//    printf("玩家%d的消息队列情况:",id);
//    for (int i = 0; i < MAX_QUEUE; ++i) {
//        if(ClientQueueBusy[id][i]) {
//            printf("1");
//        } else {
//            printf("0");
//        }
//    }
//    printf("\n");
    for (int i = 0; i < MAX_QUEUE; ++i) {
        if(ClientQueueBusy[id][i])
            continue;
        strcpy(ClientSendQueue[id][i],data);
        //printf("[S](id=%d)Queued into %d : %s\n",id,i,ClientSendQueue[id][i]);
        ClientQueueBusy[id][i] = true;
        return true;
    }
    return false; //当队列满时，该消息将会被丢弃，此时由调用者权衡该怎么做，踢出该玩家还是怎么样
}

//阻塞式接受新客户端连接
//接受到新客户端连接时，在全局变量保存ClientData，newClientCount++,IDClientOnline设true
//      同时为新客户端开两个线程，一个用于接受其消息，另一个以队列形式发送要发给该客户端的消息
int waitAndDistributeClient(void* arg)
{
    SOCKET clientSocket;
    struct sockaddr_in clientAddr;
    int clientAddrLen = sizeof(clientAddr); //我推测在不同平台下这个len不一样大，所以要动态获取
    while (ServerOn) {
        // 接受客户端连接
        if ((clientSocket = accept(listenSocket, (struct sockaddr*)&clientAddr, &clientAddrLen)) == INVALID_SOCKET) {
            printf("[S]Failed to accept client connection\n");
            continue;
        }
        if (nowClientCount >= MAX_ClIENT) { //人满了
            closesocket(clientSocket);
            continue;
        }
        for (int i = 0; i < MAX_ClIENT; ++i) {
            if(!IDClientOnline[i]) {
                clientDatas[i] = new ClientData{i, clientSocket};
                clientDatas[i]->sock = clientSocket;
                mtx_lock(&locker);
                nowClientCount++;
                mtx_unlock(&locker);
                IDClientOnline[i] = true;
                printf("[S]Client connected: %s(id=%d),now there's %d client.\n", inet_ntoa(clientAddr.sin_addr),i,nowClientCount);
                // 在新线程中处理客户端
                thrd_t clientListener,clientSender;
                thrd_create(&clientListener,listenAndHandleClient,(void*)clientDatas[i]);
                thrd_create(&clientSender,asyncSendQueueToClient,(void*)clientDatas[i]);
                break;
            }
        }
    }
    return 0;
}

//接收客户端消息，并在消息到来时调用注册的回调函数
//
int listenAndHandleClient(void* arg)
{
    ClientData *clientData = (ClientData*) arg;
    SOCKET clientSocket = clientData->sock;
    int FailCount = 0;
    char buffer[128];
    int recLen = 0;
//    sprintf(buffer,"You are %d",clientData->id);        这些都是耦合room的东西，不应该出现在network/server里
//    if (!QueueSendDataToClient(clientData->id,buffer)) {
//        printf("Failed to Initial Info to client\n");
//    }
    while (ServerOn) {
        FailCount = 0;
        // 接收客户端消息
        memset(buffer, 0, sizeof(buffer));
        //这个函数的参数128一定要写对，不能比buffer大，我付出了惨痛的代价
        while (ServerOn) {
            recLen = recv(clientSocket, buffer, 128, 0);
            if (!ServerOn)
                return 1;
            if (recLen == SOCKET_ERROR) {
                if (FailCount>2) {
                    KickClient(clientData->id);
                    return 1;
                }
                FailCount++;
                printf("[S]Failed to receive client message(%d/3) Error code %d\n",FailCount,WSAGetLastError());
                Sleep(1000);
                continue;
            }
            if (recLen == 0) {
                KickClient(clientData->id);
                return 1;
            }
            FailCount = 0;
            break;
        }
        //printf("[S] <- client: %s\n", buffer);
        char *tempP[回调函数的数量];
        for (int i = 0; i < 回调函数的数量; ++i) {
            tempP[i] = buffer;
            收到客户端消息时的回调函数[i](new ClientMessage{clientData->id,tempP[i]});
        }
    }
    return 0;
}

//不断循环遍历自己的消息队列，并向客户端发送信息
int asyncSendQueueToClient(void* arg)
{
    ClientData* myData = (ClientData*)arg;
    int id = myData->id;
    char (*mySendQueue)[64] = ClientSendQueue[id];//一个指向char[64]的指针，指向自己的输出缓冲队列
    int mySendPointer = 0;
    int failCount = 0;
    //ClientQueueBusy只能由外部填入数据后置1，由此处发完数据后置0
    //发送成功后mySendPointer移动到下一位
    //由此可以保证线程安全
    while(ServerOn) {
        if (!IDClientOnline[id])
            return 1;
        if(ClientQueueBusy[id][mySendPointer]) { //有数据就发完再往后，没数据就往后
            while (send(myData->sock, mySendQueue[mySendPointer], strlen(mySendQueue[mySendPointer]), 0) <= 0) {
                failCount++;
                printf("[S]Failed to send message to client %d (%d/10)\n",id,failCount);
                if(failCount >= 10) {
                    KickClient(id);
                    return 1;
                }
            }
            failCount = 0;
            //printf("[S](Q %d) -> client %d: %s\n",mySendPointer,id,mySendQueue[mySendPointer]);
            //上下两句如果顺序调换，可能会两次输出一样的结果，想想为什么
            ClientQueueBusy[id][mySendPointer] = false;
            //答案：在第一次ClientQueueBusy[id][mySendPointer] = false;后第二条消息刚好进入队列的第一项，则此时下一句printf的已是新消息而不是这次发送的消息
        }
        mySendPointer++;
        if (mySendPointer == MAX_QUEUE) {
            mySendPointer = 0;
            Sleep(1);
        }
    }
    return 0;
}

bool BroadcastDataToClientExcept(char *data, int exceptID) {
    bool ok = true;
    int fullCount = 0;
    for (int i = 0; i < MAX_ClIENT; i++)
    {
        if (IDClientOnline[i] && i != exceptID)
            if (!QueueSendDataToClient(i, data)) {
                ok = false;
                fullCount++;
            }
    }
    return ok;
}

//给queueFulledClients传入一个int指针的引用，将会使该指针指向一个int数组，按顺序保存发送失败的客户端的id，以-1结尾，需要自己delete释放内存
//只有所有人都发送成功时返回true
bool BroadcastDataToClientExcept(char *data, int exceptID, int* &queueFulledClients) {
    bool ok = true;
    queueFulledClients = nullptr;
    int fullCount = 0;
    for (int i = 0; i < MAX_ClIENT; i++)
    {
        if (IDClientOnline[i] && i != exceptID)
            if (!QueueSendDataToClient(i, data)) {
                ok = false;
                if (queueFulledClients == nullptr) {
                    queueFulledClients = new int[MAX_ClIENT+1];
                }
                queueFulledClients[fullCount] = i;
                fullCount++;
            }
    }
    if(queueFulledClients != nullptr)
        queueFulledClients[fullCount] = -1;
    return ok;
}

bool RegisterClientMessageHandler(void(*callback)(ClientMessage*)) {
    if (回调函数的数量 == CLIENT_MSG_HANDLER_MAX)
        return false;
    收到客户端消息时的回调函数[回调函数的数量] = callback;
    回调函数的数量++;
    return true;
}

void SetClientDisconnectHandler(void(*callback)(int)) {
    onClientDisconnect = callback;
}

int GetClientCount() {
    return nowClientCount;
}

bool IsClientOnline(int id) {
    return 0 <= id && id < MAX_ClIENT && IDClientOnline[id];
}
//断开与客户端的连接，使用线程锁来保证nowClientCount的正确性
void KickClient(int id) {
    if (id < 0 || id >= MAX_ClIENT)
        return;
    if(!IDClientOnline[id])
        return;
    closesocket(clientDatas[id]->sock);
    IDClientOnline[id] = false;
    mtx_lock(&locker);
    nowClientCount--;
    mtx_unlock(&locker);
    if (onClientDisconnect)
        onClientDisconnect(id);
    printf("[S]Client %d has disconnected,now %d client left.\n",id,nowClientCount);
}